Skip to content

Conversation

diggerlin
Copy link
Contributor

@diggerlin diggerlin commented Aug 14, 2025

AIX has "millicode" routines, which are functions loaded at boot time into fixed addresses in kernel memory. This allows them to be customized for the processor. The __strlen routine is a millicode implementation; we use millicode for the strlen function instead of a library call to improve performance.

@llvmbot
Copy link
Member

llvmbot commented Aug 14, 2025

@llvm/pr-subscribers-backend-systemz

@llvm/pr-subscribers-backend-powerpc

Author: zhijian lin (diggerlin)

Changes

Full diff: https://github.com/llvm/llvm-project/pull/153600.diff

11 Files Affected:

  • (modified) llvm/include/llvm/CodeGen/SelectionDAG.h (+7-3)
  • (modified) llvm/include/llvm/CodeGen/SelectionDAGTargetInfo.h (+1-1)
  • (modified) llvm/include/llvm/IR/RuntimeLibcalls.td (+3)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp (+36)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp (+2-3)
  • (modified) llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.cpp (+8)
  • (modified) llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.h (+3)
  • (modified) llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.cpp (+1-1)
  • (modified) llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.h (+1-2)
  • (modified) llvm/test/CodeGen/PowerPC/milicode32.ll (+1-1)
  • (modified) llvm/test/CodeGen/PowerPC/milicode64.ll (+1-1)
diff --git a/llvm/include/llvm/CodeGen/SelectionDAG.h b/llvm/include/llvm/CodeGen/SelectionDAG.h
index dc00db9daa3b6..831e274b22ec4 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAG.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAG.h
@@ -1256,9 +1256,13 @@ class SelectionDAG {
   /// stack arguments from being clobbered.
   LLVM_ABI SDValue getStackArgumentTokenFactor(SDValue Chain);
 
-  std::pair<SDValue, SDValue> getMemcmp(SDValue Chain, const SDLoc &dl,
-                                        SDValue Dst, SDValue Src, SDValue Size,
-                                        const CallInst *CI);
+  LLVM_ABI std::pair<SDValue, SDValue> getMemcmp(SDValue Chain, const SDLoc &dl,
+                                                 SDValue Dst, SDValue Src,
+                                                 SDValue Size,
+                                                 const CallInst *CI);
+  LLVM_ABI std::pair<SDValue, SDValue>
+  getStrlen(SDValue Chain, const SDLoc &dl, SDValue Src, const CallInst *CI);
+
   /* \p CI if not null is the memset call being lowered.
    * \p OverrideTailCall is an optional parameter that can be used to override
    * the tail call optimization decision. */
diff --git a/llvm/include/llvm/CodeGen/SelectionDAGTargetInfo.h b/llvm/include/llvm/CodeGen/SelectionDAGTargetInfo.h
index fd00f813bc9c3..fbfb240cae449 100644
--- a/llvm/include/llvm/CodeGen/SelectionDAGTargetInfo.h
+++ b/llvm/include/llvm/CodeGen/SelectionDAGTargetInfo.h
@@ -162,7 +162,7 @@ class SelectionDAGTargetInfo {
 
   virtual std::pair<SDValue, SDValue>
   EmitTargetCodeForStrlen(SelectionDAG &DAG, const SDLoc &DL, SDValue Chain,
-                          SDValue Src, MachinePointerInfo SrcPtrInfo) const {
+                          SDValue Src, const CallInst *CI) const {
     return std::make_pair(SDValue(), SDValue());
   }
 
diff --git a/llvm/include/llvm/IR/RuntimeLibcalls.td b/llvm/include/llvm/IR/RuntimeLibcalls.td
index 9072a0aa1531f..3fe98b367cb01 100644
--- a/llvm/include/llvm/IR/RuntimeLibcalls.td
+++ b/llvm/include/llvm/IR/RuntimeLibcalls.td
@@ -282,6 +282,7 @@ def MEMMOVE : RuntimeLibcall;
 def MEMSET : RuntimeLibcall;
 def CALLOC : RuntimeLibcall;
 def BZERO : RuntimeLibcall;
+def STRLEN : RuntimeLibcall;
 
 // Element-wise unordered-atomic memory of different sizes
 foreach MemSize = [1, 2, 4, 8, 16] in {
@@ -2115,6 +2116,7 @@ defset list<RuntimeLibcallImpl> PPC64AIXCallList = {
   def ___memmove64 : RuntimeLibcallImpl<MEMCPY>;
   def ___memset64 : RuntimeLibcallImpl<MEMSET>;
   def ___bzero64 : RuntimeLibcallImpl<BZERO>;
+  def ___strlen64 : RuntimeLibcallImpl<STRLEN>;
 }
 
 defset list<RuntimeLibcallImpl> PPC32AIXCallList = {
@@ -2122,6 +2124,7 @@ defset list<RuntimeLibcallImpl> PPC32AIXCallList = {
   def ___memmove : RuntimeLibcallImpl<MEMMOVE>;
   def ___memset : RuntimeLibcallImpl<MEMSET>;
   def ___bzero : RuntimeLibcallImpl<BZERO>;
+  def ___strlen : RuntimeLibcallImpl<STRLEN>;
 }
 
 defvar PPCOverrides = !foreach(entry, PPCRuntimeLibcalls, entry.Provides);
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
index 4b7fc45908119..a1f3a9b201a30 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp
@@ -9050,6 +9050,42 @@ SelectionDAG::getMemcmp(SDValue Chain, const SDLoc &dl, SDValue Mem0,
   return TLI->LowerCallTo(CLI);
 }
 
+std::pair<SDValue, SDValue> SelectionDAG::getStrlen(SDValue Chain,
+                                                    const SDLoc &dl,
+                                                    SDValue Src,
+                                                    const CallInst *CI) {
+  const char *LibCallName = TLI->getLibcallName(RTLIB::STRLEN);
+  if (!LibCallName)
+    return {};
+
+  // Emit a library call.
+  auto GetEntry = [](Type *Ty, SDValue &SDV) {
+    TargetLowering::ArgListEntry E;
+    E.Ty = Ty;
+    E.Node = SDV;
+    return E;
+  };
+
+  PointerType *PT = PointerType::getUnqual(*getContext());
+  TargetLowering::ArgListTy Args = {GetEntry(PT, Src)};
+
+  TargetLowering::CallLoweringInfo CLI(*this);
+  bool IsTailCall = false;
+  bool ReturnsFirstArg = CI && funcReturnsFirstArgOfCall(*CI);
+  IsTailCall = CI && CI->isTailCall() &&
+               isInTailCallPosition(*CI, getTarget(), ReturnsFirstArg);
+
+  CLI.setDebugLoc(dl)
+      .setChain(Chain)
+      .setLibCallee(
+          TLI->getLibcallCallingConv(RTLIB::STRLEN), CI->getType(),
+          getExternalSymbol(LibCallName, TLI->getPointerTy(getDataLayout())),
+          std::move(Args))
+      .setTailCall(IsTailCall);
+
+  return TLI->LowerCallTo(CLI);
+}
+
 SDValue SelectionDAG::getMemcpy(
     SDValue Chain, const SDLoc &dl, SDValue Dst, SDValue Src, SDValue Size,
     Align Alignment, bool isVol, bool AlwaysInline, const CallInst *CI,
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
index 366a230eef952..9a75f417e2af8 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp
@@ -9287,9 +9287,8 @@ bool SelectionDAGBuilder::visitStrLenCall(const CallInst &I) {
   const Value *Arg0 = I.getArgOperand(0);
 
   const SelectionDAGTargetInfo &TSI = DAG.getSelectionDAGInfo();
-  std::pair<SDValue, SDValue> Res =
-    TSI.EmitTargetCodeForStrlen(DAG, getCurSDLoc(), DAG.getRoot(),
-                                getValue(Arg0), MachinePointerInfo(Arg0));
+  std::pair<SDValue, SDValue> Res = TSI.EmitTargetCodeForStrlen(
+      DAG, getCurSDLoc(), DAG.getRoot(), getValue(Arg0), &I);
   if (Res.first.getNode()) {
     processIntegerCallValue(I, Res.first, false);
     PendingLoads.push_back(Res.second);
diff --git a/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.cpp b/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.cpp
index 4039fedd0cb5c..e15b5b0bea2fc 100644
--- a/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.cpp
+++ b/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.cpp
@@ -28,3 +28,11 @@ std::pair<SDValue, SDValue> PPCSelectionDAGInfo::EmitTargetCodeForMemcmp(
     SDValue Op3, const CallInst *CI) const {
   return DAG.getMemcmp(Chain, dl, Op1, Op2, Op3, CI);
 }
+
+std::pair<SDValue, SDValue>
+PPCSelectionDAGInfo::EmitTargetCodeForStrlen(SelectionDAG &DAG, const SDLoc &DL,
+                                             SDValue Chain, SDValue Src,
+                                             const CallInst *CI) const {
+  EVT PtrVT = Src.getValueType();
+  return DAG.getStrlen(Chain, DL, Src, CI);
+}
diff --git a/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.h b/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.h
index 1537851a1b610..f962a7a5321aa 100644
--- a/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.h
+++ b/llvm/lib/Target/PowerPC/PPCSelectionDAGInfo.h
@@ -25,6 +25,9 @@ class PPCSelectionDAGInfo : public SelectionDAGTargetInfo {
   EmitTargetCodeForMemcmp(SelectionDAG &DAG, const SDLoc &dl, SDValue Chain,
                           SDValue Op1, SDValue Op2, SDValue Op3,
                           const CallInst *CI) const override;
+  std::pair<SDValue, SDValue>
+  EmitTargetCodeForStrlen(SelectionDAG &DAG, const SDLoc &DL, SDValue Chain,
+                          SDValue Src, const CallInst *CI) const override;
 };
 
 } // namespace llvm
diff --git a/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.cpp b/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.cpp
index afe838ac973e6..eb00d484af693 100644
--- a/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.cpp
@@ -263,7 +263,7 @@ static std::pair<SDValue, SDValue> getBoundedStrlen(SelectionDAG &DAG,
 
 std::pair<SDValue, SDValue> SystemZSelectionDAGInfo::EmitTargetCodeForStrlen(
     SelectionDAG &DAG, const SDLoc &DL, SDValue Chain, SDValue Src,
-    MachinePointerInfo SrcPtrInfo) const {
+    const CallInst *CI) const {
   EVT PtrVT = Src.getValueType();
   return getBoundedStrlen(DAG, DL, Chain, Src, DAG.getConstant(0, DL, PtrVT));
 }
diff --git a/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.h b/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.h
index 5a1e0cd108e77..200566f9646c1 100644
--- a/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.h
+++ b/llvm/lib/Target/SystemZ/SystemZSelectionDAGInfo.h
@@ -61,8 +61,7 @@ class SystemZSelectionDAGInfo : public SelectionDAGTargetInfo {
 
   std::pair<SDValue, SDValue>
   EmitTargetCodeForStrlen(SelectionDAG &DAG, const SDLoc &DL, SDValue Chain,
-                          SDValue Src,
-                          MachinePointerInfo SrcPtrInfo) const override;
+                          SDValue Src, const CallInst *CI) const override;
 
   std::pair<SDValue, SDValue>
   EmitTargetCodeForStrnlen(SelectionDAG &DAG, const SDLoc &DL, SDValue Chain,
diff --git a/llvm/test/CodeGen/PowerPC/milicode32.ll b/llvm/test/CodeGen/PowerPC/milicode32.ll
index a2af6d413b4bf..78d036202fe4e 100644
--- a/llvm/test/CodeGen/PowerPC/milicode32.ll
+++ b/llvm/test/CodeGen/PowerPC/milicode32.ll
@@ -42,7 +42,7 @@ define i32 @strlen_test(ptr noundef %str) nounwind {
 ; CHECK-AIX-32-P9-NEXT:    stwu r1, -64(r1)
 ; CHECK-AIX-32-P9-NEXT:    stw r0, 72(r1)
 ; CHECK-AIX-32-P9-NEXT:    stw r3, 60(r1)
-; CHECK-AIX-32-P9-NEXT:    bl .strlen[PR]
+; CHECK-AIX-32-P9-NEXT:    bl .___strlen[PR]
 ; CHECK-AIX-32-P9-NEXT:    nop
 ; CHECK-AIX-32-P9-NEXT:    addi r1, r1, 64
 ; CHECK-AIX-32-P9-NEXT:    lwz r0, 8(r1)
diff --git a/llvm/test/CodeGen/PowerPC/milicode64.ll b/llvm/test/CodeGen/PowerPC/milicode64.ll
index 0f0585d9028a9..8b87529d9a6d8 100644
--- a/llvm/test/CodeGen/PowerPC/milicode64.ll
+++ b/llvm/test/CodeGen/PowerPC/milicode64.ll
@@ -85,7 +85,7 @@ define i64 @strlen_test(ptr noundef %str) nounwind {
 ; CHECK-AIX-64-P9-NEXT:    stdu r1, -128(r1)
 ; CHECK-AIX-64-P9-NEXT:    std r0, 144(r1)
 ; CHECK-AIX-64-P9-NEXT:    std r3, 120(r1)
-; CHECK-AIX-64-P9-NEXT:    bl .strlen[PR]
+; CHECK-AIX-64-P9-NEXT:    bl .___strlen64[PR]
 ; CHECK-AIX-64-P9-NEXT:    nop
 ; CHECK-AIX-64-P9-NEXT:    addi r1, r1, 128
 ; CHECK-AIX-64-P9-NEXT:    ld r0, 16(r1)

.setChain(Chain)
.setLibCallee(
TLI->getLibcallCallingConv(RTLIB::STRLEN), CI->getType(),
getExternalSymbol(LibCallName, TLI->getPointerTy(getDataLayout())),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
getExternalSymbol(LibCallName, TLI->getPointerTy(getDataLayout())),
getExternalSymbol(LibCallName, TLI->getProgramPointerTy(getDataLayout())),

PPCSelectionDAGInfo::EmitTargetCodeForStrlen(SelectionDAG &DAG, const SDLoc &DL,
SDValue Chain, SDValue Src,
const CallInst *CI) const {
EVT PtrVT = Src.getValueType();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable?

Comment on lines 9075 to 9076
IsTailCall = CI && CI->isTailCall() &&
isInTailCallPosition(*CI, getTarget(), ReturnsFirstArg);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume none of the tail call handling is untested like the other functions? Can this code be shared between these similar calls?

Copy link
Contributor Author

@diggerlin diggerlin Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume none of the tail call handling is untested like the other functions? Can this code be shared between these similar calls?

I deleted the Tail call code and add TODO comment for it. and I will add same TODO comment for the function "SelectionDAG::getMemcmp" and delete the tail call code in a NFC patch later.

Or I move the TODO comment to just before the function SelectionDAG::getMemcmp to let the "TODO" comment both for function SelectionDAG::getMemcmp and SelectionDAG::getStrlen ?

@diggerlin diggerlin requested a review from arsenm August 20, 2025 17:49
std::pair<SDValue, SDValue> getMemcmp(SDValue Chain, const SDLoc &dl,
SDValue Dst, SDValue Src, SDValue Size,
const CallInst *CI);
LLVM_ABI std::pair<SDValue, SDValue> getMemcmp(SDValue Chain, const SDLoc &dl,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated change.

// Why:
// - The only current in-tree user of SelectionDAG::getStrlen is the AIX path,
// where generic tail-calling to libcalls is not safe due to ABI
// constraints around r2 (TOC). We don¡¯t have a reliable way to validate a
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo on don't


TargetLowering::CallLoweringInfo CLI(*this);

// TODO: Intentionally not marking this libcall as a tail call.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a TODO should be something that remains to be done. Suggest TODO: propagate tail call flag for targets where that is safe. Note that it is not safe on AIX which is the only current target.

Comment on lines +9074 to +9075
// TODO: propagate tail call flag for targets where that is safe. Note
// that it is not safe on AIX which is the only current target.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant can you just factor out the existing check into a helper function that all the cases use

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created an NFC patch [NFC ]Add a helper function isTailCall for getting libcall in SelectionDAG, After the NFC is approved, I will modify the patch accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants